/**
 *  \file window.cpp
 *  \brief Curses++ windows header.
 *
 *  The implementation of the Curses window interface.
 *
 *  \if LICENSE
 *  Curses++ - Free C++ wrapper to the curses library.
 *  Copyright (C) 2007 Sean Hunt
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330,
 *  Boston, MA 02111-1307, USA.
 *  \endif
 */
#include "cursesplusplus.h"
#include "curses-local.hpp"	

namespace curses
{
	struct window::priv_data
	{
		WINDOW * win;
		uint ref_count;
	};

	const window::attribute window::echo;
	const window::attribute window::line_buffer;
	const window::attribute window::keypad;
	const window::attribute window::flush_interrupt;
	const window::attribute window::newline;

	window::window ()
		: data (new window::priv_data())
	{
		data->ref_count = 1;
	}

	void window::print (const string& text, unsigned int x, unsigned int y)
	{
		chtype * c = get_curses(text);
		mvwaddchnstr(this->data->win, y, x, c, text.size());
		delete[] c;
	}

	void window::print (const string& text)
	{
		chtype * c = get_curses(text);
		waddchnstr(this->data->win, c, text.size());
		delete[] c;
	}

	void window::clear ()
	{
		wclear(this->data->win);
	}

	void window::update ()
	{
		wrefresh(this->data->win);
	}

	character window::get ()
	{
		return from_curses(winch(this->data->win));
	}

	character window::get (unsigned int x, unsigned int y)
	{
		unsigned int cx, cy;
		this->get_cursor(cx, cy);
		character c = from_curses(mvwinch(this->data->win, y, x));
		this->move_cursor(cx, cy);
		return c;
	}

	void window::move_cursor (unsigned int x, unsigned int y)
	{
		wmove(this->data->win, y, x);
	}

	void window::get_cursor (unsigned int& x, unsigned int& y)
	{
		int cx, cy;
		getyx(this->data->win, cy, cx);
		x = cx;
		y = cy;
	}

	void window::hide_cursor ()
	{
		curs_set(0);
	}

	void window::show_cursor ()
	{
		curs_set(1);
	}

	void window::bright_cursor ()
	{
		curs_set(2);
	}

	void window::del (unsigned int x, unsigned int y)
	{
		unsigned int cx, cy;
		this->get_cursor(cx, cy);
		mvwdelch(this->data->win, y, x);
		this->move_cursor(cx, cy);
	}

	void window::pause ()
	{
		endwin();
	}

	window window::subwin (unsigned int x, unsigned int y, unsigned int w, unsigned int h)
	{
		window new_win;
		new_win.data->win = ::subwin(this->data->win, h, w, y, x);
		return new_win;
	}

	window::window (const window& win)
		: data (win.data)
	{
		++(this->data->ref_count);
	}

	window::~window ()
	{
		if (--(this->data->ref_count) == 0)
		{
			if (this->data->win == stdscr)
			{
				endwin();
			}
			delwin(this->data->win);
		}
	}

	void window::set_attrs (attribute attr)
	{
		if (attr & echo)
			::echo();
		else
			noecho();

		if (attr & line_buffer)
			nocbreak();
		else
			cbreak();

		::keypad(this->data->win, attr & keypad);

		if (attr & flush_interrupt)
			qiflush();
		else
			noqiflush();

		if (attr & newline)
			nl();
		else
			nonl();
	}

	keycode window::get_key ()
	{
		int code = getch();
		if (code < 256 && code >= 0)
			return static_cast<keycode>(code);

		switch (code)
		{
			case EOF: return key::eof;
			case KEY_BREAK: return key::break_key;
			case KEY_DOWN: return key::down;
			case KEY_UP: return key::up;
			case KEY_LEFT: return key::left;
			case KEY_RIGHT: return key::right;
			case KEY_HOME: return key::home;
			case KEY_BACKSPACE: return key::backspace;
			case KEY_DL: return key::delete_line;
			case KEY_IL: return key::insert_line;
			case KEY_DC: return key::delete_character;
			case KEY_IC: return key::insert_character;
			case KEY_EIC: return key::exit_insert_mode;
			case KEY_CLEAR: return key::clear;
			case KEY_EOS: return key::clear_end_screen;
			case KEY_EOL: return key::clear_end_line;
			case KEY_SF: return key::scroll_forward;
			case KEY_SR: return key::scroll_backward;
			case KEY_NPAGE: return key::page_forward;
			case KEY_PPAGE: return key::page_backward;
			case KEY_STAB: return key::set_tab;
			case KEY_CTAB: return key::clear_tab;
			case KEY_CATAB: return key::clear_all_tabs;
			case KEY_ENTER: return key::enter;
			case KEY_SRESET: return key::soft_reset;
			case KEY_RESET: return key::reset;
			case KEY_PRINT: return key::print;
			case KEY_LL: return key::home_bottom;
			case KEY_A1: return key::keypad_top_left;
			case KEY_A3: return key::keypad_top_right;
			case KEY_B2: return key::keypad_middle;
			case KEY_C1: return key::keypad_bottom_left;
			case KEY_C3: return key::keypad_bottom_right;
			default: return get_key();
		}
	}

	window window::start (attribute attr)
	{
		window the_win;
		initscr();
		prepare_color();
		prepare_locale();
		the_win.data->win = stdscr;

		the_win.set_attrs(attr);
		return the_win;
	}
}
